﻿using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace FreeDream.DBUtility
{
    public class BlogPlatDbContext : DbContext
    {
        #region 部分说明

        //管理数据库架构的 EnsureCreated()
        //EnsureDeleted 方法将删除数据库（如果存在）。 如果没有适当的权限，则会引发异常。
        //// Drop the database if it exists
        //dbContext.Database.EnsureDeleted();
        //EnsureCreated 将创建数据库（如果不存在）并初始化数据库架构。 如果存在任何表(包含另一个 DbContext 类的) ，则架构不会初始化。
        //// Create the database if it doesn't exist
        //dbContext.Database.EnsureCreated();
        //若要获取SQL，EnsureCreated可以使用 GenerateCreateScript 方法。
        //var sql = dbContext.Database.GenerateCreateScript();

        #endregion

        ////Microsoft.Extensions.Logging.Console
        //private static readonly ILoggerFactory _loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());

        protected BlogPlatDbContext()
        {
        }

        public BlogPlatDbContext(DbContextOptions options) : base(options)
        {
        }

        #region 重载

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                optionsBuilder.UseSqlServer(@"Server=.;Database=BlogPlat;uid=sa;pwd=123");
            }
            ////optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True");
            ////延迟加载实现包--Microsoft.EntityFrameworkCore.Proxies
            ////optionsBuilder.UseLazyLoadingProxies(false);
            ///

            //optionsBuilder.UseSqlServer(ConnectionString, p => p.CommandTimeout(GlobalContext.SystemConfig.DBCommandTimeout));
            ////optionsBuilder.AddInterceptors(new DbCommandCustomInterceptor());
            //optionsBuilder.UseLoggerFactory(_loggerFactory);
            //// 这里需要注意，不能采用这种写法：optionsBuilder.UseLoggerFactory(LoggerFactory.Create(builder => builder.AddConsole()));
            //// 会导致内存泄露的问题
        }

        /// <summary>
        /// 加入所有实体类
        /// </summary>
        /// <param name="modelBuilder"></param>
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            Assembly entityAssembly = Assembly.Load(new AssemblyName("Dn.Entity"));
            IEnumerable<Type> typesToRegister = entityAssembly.GetTypes().Where(p => !string.IsNullOrEmpty(p.Namespace))
                                                                         .Where(p => !string.IsNullOrEmpty(p.GetCustomAttribute<TableAttribute>()?.Name));
            foreach (Type type in typesToRegister)
            {
                dynamic configurationInstance = Activator.CreateInstance(type);
                modelBuilder.Model.AddEntityType(type);
            }
            foreach (var entity in modelBuilder.Model.GetEntityTypes())
            {
                //主键约定，把属性Id当做数据库主键
                modelBuilder.Entity(entity.Name).HasKey("Id");

                string currentTableName = modelBuilder.Entity(entity.Name).Metadata.GetTableName();
                modelBuilder.Entity(entity.Name).ToTable(currentTableName);

                //var properties = entity.GetProperties();
                //foreach (var property in properties)
                //{
                //    ColumnConvention.SetColumnName(modelBuilder, entity.Name, property.Name);
                //}
            }

            base.OnModelCreating(modelBuilder);


            modelBuilder.ApplyConfiguration(new BlogEntityTypeConfiguration());
        }

        /// <summary>
        /// 为了减小 OnModelCreating 方法的大小，可以将实体类型的所有配置提取到实现 IEntityTypeConfiguration<TEntity> 的单独类中
        /// 然后，只需从 OnModelCreating 调用 Configure 方法。
        /// </summary>
        public class BlogEntityTypeConfiguration : IEntityTypeConfiguration<Blog>
        {
            public void Configure(EntityTypeBuilder<Blog> builder)
            {
                builder.Property(b => b.Url).IsRequired();
            }
        }

        /// <summary>
        /// 列名约定，比如属性ParentId，映射到数据库字段parent_id
        /// </summary>
        [Obsolete]
        public class ColumnConvention
        {
            public static void SetColumnName(ModelBuilder modelBuilder, string entityName, string propertyName)
            {
                StringBuilder sbField = new StringBuilder();
                char[] charArr = propertyName.ToCharArray();

                int iCapital = 0; // 把属性第一个开始的大写字母转成小写，直到遇到了第1个小写字母，因为数据库里面是小写的
                while (iCapital < charArr.Length)
                {
                    if (charArr[iCapital] >= 'A' && charArr[iCapital] <= 'Z')
                    {
                        charArr[iCapital] = (char)(charArr[iCapital] + 32);
                    }
                    else
                    {
                        break;
                    }
                    iCapital++;
                }

                for (int i = 0; i < charArr.Length; i++)
                {
                    if (charArr[i] >= 'A' && charArr[i] <= 'Z')
                    {
                        charArr[i] = (char)(charArr[i] + 32);
                        sbField.Append("_" + charArr[i]);
                    }
                    else
                    {
                        sbField.Append(charArr[i]);
                    }
                }
                modelBuilder.Entity(entityName).Property(propertyName).HasColumnName(sbField.ToString());
            }
        }

        #endregion

    }
}
